Dir["#{$root_folder}/models/*.rb"].each { |file| require file }
Dir["#{$root_folder}/lib/core_ext/*.rb"].each { |file| require file }
require "twitter"
require "mongo_mapper"
require "date"
require 'mustache'

class Tbot
  attr_accessor :settings, :account, :v, :previous_followers, :all_friends, :all_followers

  def initialize(settings)
    @settings = settings
    if @settings['verbosity'] == nil
      @v = 0
    else
      @v = @settings['verbosity'].to_i
    end
    puts "Verbosity = #{@v}" if @v > 2
    puts @settings.inspect if @v > 3

    raise "No twitter username in YAML file" if @settings['twitter']['name'] == nil

    @account = Account.first(:name => @settings['twitter']['name'])
    if @account == nil
      puts "Creating new account: #{@settings['twitter']['name']}" if v > 1
      @account = Account.new(:name => @settings['twitter']['name'])
    end
    if @account.id == nil
      Log.new(:type => "Error", :message => "No Account ID").save
      raise "Failed to create a new account"
    end

    puts "Creating bot for account #{@account.name}: #{@account.id}" if v > 1

    begin
      # Certain methods require authentication. To get your Twitter OAuth credentials, register an app at http://dev.twitter.com/apps
      Twitter.configure do |config|
        config.consumer_key = @settings['twitter']['consumer_key']
        config.consumer_secret = @settings['twitter']['consumer_secret']
        config.oauth_token = @settings['twitter']['oauth_token']
        config.oauth_token_secret = @settings['twitter']['oauth_token_secret']
      end
    rescue Exception => e
      Log.new(:exception => e.to_s, :message => e.class.to_s).save
      puts "Exception: #{e}"
      puts e.inspect
      exit
    end

    @previous_followers = @account.follower_ids

    begin
        @all_followers = Twitter.follower_ids.ids
        @all_friends = Twitter.friend_ids.ids
    rescue Twitter::Error::TooManyRequests => error
        abort("Api requests exhausted")
    end

    @account.follower_ids = @all_followers
    @account.friend_ids = @all_friends

    @account.save
  end

  def check_unfollow()
    puts "Checking #{@account.id} for stagnant followers" if @v > 1

    if @settings["karma"] == nil
      puts "No kama settings - skipping unfollow check" if @v > 2
      return
    end
    karma = @settings["karma"]
    days_until_unfollow = karma["days_until_unfollow"] != nil ? karma["days_until_unfollow"].to_i : 30
    puts "Kama (#{days_until_unfollow}):" if @v > 2

    #Find people wo haven't followed us back
    dif = @all_friends - @all_followers
    dif.each do |follower_id|

      friend = Tweeter.first(:twitter_id => follower_id, :account_id => @account.id)
      if friend == nil
        #New Friend
        user = nil
        begin
          user = Twitter.user(follower_id)
        rescue Twitter::Error::TooManyRequests => error
            abort("Api requests exhausted")
        rescue Twitter::Error::Forbidden
          puts "Got User (#{follower_id.to_s}) is Forbidden when checking unfollow"
          Tweeter.new(
              :twitter_id => follower_id,
              :name => "Forbidden",
              :suspended => true,
              :following => false,
              :account_id => @account.id
          ).save
          next
        rescue Exception => e
          Log.new(:exception => e.to_s, :message => e.class.to_s).save
          puts "Exception: #{e}"
          puts "e.class: #{e.class}"
          puts e.inspect
        end

        if user != nil
          puts "Saving friend: #{user.name} (#{user.screen_name})" if @v > 1
          friend = Tweeter.new(
              :twitter_id => follower_id,
              :screen_name => user.screen_name,
              :name => user.name,
              :location => user.location,
              :account_id => @account.id
          )
          friend.save
        else
          puts "Can not retrieve data for: #{follower_id}"
        end
      else
        #Existing Friend
        if friend.suspended != true
          puts "UserID: #{friend.id}" if @v > 3

          days_since_follow = (DateTime.now - friend.followed_on.to_datetime).to_i
          days_to_unfollow = days_until_unfollow - days_since_follow

          if days_to_unfollow <= 0
            begin
                user = Twitter.user(follower_id)
            rescue Twitter::Error::TooManyRequests => error
                abort("Api requests exhausted")
            end

            if !karma["do_not_unfollow"].include?(user.screen_name)
                begin
                  Twitter.unfollow(follower_id)
                rescue Twitter::Error::TooManyRequests => error
                    abort("Api requests exhausted")
                end

                friend.following = false
                friend.follower = false
                friend.unfollowed = true
                friend.save

                Log.new(
                      :type => "Unfollow",
                      :message => "Unfollowed #{user.screen_name} (#{follower_id}) - days_to_unfollow : #{days_to_unfollow} - days_since_follow : #{days_since_follow} - days_until_unfollow : #{days_until_unfollow} - friend.followed_on : #{friend.followed_on} - Time.now : #{Time.now}"
                ).save
                puts "Unfollowed #{user.screen_name}"
            end
          else
            if !karma["do_not_unfollow"].include?(friend.screen_name)
              puts "Not ready to unfollow #{friend.screen_name} un-following in #{days_to_unfollow} days" if @v > 2
            end
          end
        else
          puts "#{friend.twitter_id} is suspended" if @v > 3
        end
      end
    end
  end

  def find_friends_by_keyword(searchPhrase)
    puts "Searching: #{searchPhrase}" if v > 1
    keyword = Keyword.first(:name => searchPhrase, :account_id => @account.id)
    if keyword == nil
      keyword = Keyword.new(:name => searchPhrase)
      keyword.account_id = @account.id
      puts "Creating new keyword #{searchPhrase} on account #{@account.id}" if @v > 0
    end

    last_tweet_id = keyword.last_tweet_id != nil ? keyword.last_tweet_id : 0
    resultSize = @settings['search']['max_records'] != nil ? @settings['search']['max_records'] : 25

    puts "Finding #{resultSize} tweets that contain: #{searchPhrase} since #{last_tweet_id}" if @v > 2
    Twitter.search(searchPhrase, :rpp => resultSize, :result_type => "recent", :since_id => last_tweet_id).results.map do |tweet|
      #http://rdoc.info/gems/twitter/Twitter/Tweet
      begin
        puts ""
        puts tweet.text

        #  Have we befriended them previously
        friend = Tweeter.first(:twitter_id => tweet.from_user_id, :account_id => @account.id)

        if friend == nil
          #Dont try to follow ourselves
          
          next if tweet.from_user.downcase == @settings['twitter']['name'].downcase

          isFriend = @all_friends.include?(tweet.from_user_id)
          if isFriend
            puts "We already following: #{tweet.from_user}"
          else
            puts "Befriending: #{tweet.from_user}"
            Twitter.follow(tweet.from_user)
          end

          #Create new local user
          friend = Tweeter.new(
              :twitter_id => tweet.from_user_id,
              :screen_name => tweet.from_user,
              :name => tweet.from_user,
              :keyword => searchPhrase,
              :tweet => tweet.text,
              :tweet_id => tweet.id,
              :geo => tweet.geo.to_s,
              :account_id => @account.id
          )
          friend.save
          puts "Saving #{friend.name} to account: #{@account.id}" if @v > 1
        else
          puts "#{friend.name} is already a friend"
        end

        last_tweet_id = tweet.id if tweet.id > last_tweet_id
      rescue Twitter::Error::TooManyRequests => error
        abort("Api requests exhausted")
      rescue Twitter::Error::Forbidden
        puts "Got User (#{tweet.from_user_id}) is Forbidden while searching for #{searchPhrase}"
        Tweeter.new(
            :twitter_id => tweet.from_user_id,
            :name => "Forbidden",
            :following => false,
            :account_id => @account.id
        ).save
        next
      rescue Twitter::Error::BadRequest
        abort("Api requests exhausted")
      rescue Exception => e
        Log.new(:message => e.class.to_s, :exception => e.to_s).save
        puts "Exception: #{e}"
        puts "e.class: #{e.class}"
        puts e.inspect
      end
    end

    puts "Latest tweet ID = #{last_tweet_id}" if @v > 2
    keyword.last_tweet_id = last_tweet_id

    if !keyword.save
      Log.new(:type => "Warning", :message => "Error saving keyword #{k}").save
    end
  end

  def dm_new_followers()
    return if @previous_followers.empty? #Dont spam everyone on first run
    puts "Checking for new followers" if v > 1
    #Find people wo haven't followed us back
    #new_followers = @all_followers - @all_friends
    new_followers = @all_followers - @previous_followers
    new_followers.each do |follower|

     begin
        user = Twitter.user(follower)
     rescue Twitter::Error::TooManyRequests => error
        abort("Api requests exhausted")
     rescue Exception => e
        Log.new(:exception => e.to_s, :message => e.class.to_s).save
        puts "Exception: #{e}"
        puts e.inspect
     end

      #Have we got them in out DB
      friend = Tweeter.first(:twitter_id => user.id, :account_id => @account.id)
      if friend == nil
        #No so add them
        friend = Tweeter.new(
            :twitter_id => user.id,
            :screen_name => user.screen_name,
            :name => user.name,
            :location => user.location,
            :account_id => @account.id
        )
        friend.save

        puts "New Follower: #{user.name}" if @v > 2
        # Are they already a friend
        isFriend = @all_friends.include?(user.id)
        if !isFriend
          # follow is not rate limited https://dev.twitter.com/docs/api/1/post/friendships/create
          Twitter.follow(user.id)
          puts "Followed #{user.name}" if @v > 0
        end
      end
      #They are now following us
      friend.follower = true

      #Have we sent_dm then b4?
      if friend.sent_dm
        puts "Already DM'd #{friend.name} (#{friend.id})" if @v > 2
        next
      end

      dm = ""
      dm = @settings["search"]["dm_default"] if @settings["search"]["dm_default"] != nil

      puts "friend.keyword: #{friend.keyword}" if @v > 2
      if friend.keyword != nil
        keyword_settings = @settings['search']['keywords'].find_all { |item| item["keyword"] == friend.keyword }
        if keyword_settings != nil
          if keyword_settings[0]["dm"] != nil
            dm = keyword_settings[0]["dm"]
            puts "Found keyword specific DM #{dm}" if @v > 2
          else
            puts "No custom keyword DM for #{friend.keyword}" if @v > 3
          end
        end
      end

      if !dm.empty?

        begin
          dm_template = Mustache.render(dm, :first_name => friend.name.first_word)
          dm_tweet = "D @#{friend.screen_name} #{dm_template}"[0, 140]
          puts dm_tweet if @v > 0

          # update is not rate limited https://dev.twitter.com/docs/api/1/post/statuses/update
          if friend.tweet_id == nil
            Twitter.update(dm_tweet)
          else
            Twitter.update(dm_tweet, {:in_reply_to_status_id => friend.tweet_id})
          end

        rescue Exception => e
          Log.new(:exception => e.to_s, :message => e.class.to_s).save
          puts "Exception: #{e}"
          puts e.inspect
        end
        friend.sent_dm = true #Update DM status
      else
        puts "No DM" if @v > 2
      end

      if !friend.save
        Log(:type => "Warning", :message => "Failed to save #{user.screen_name} after DMing").save
      end

    end

    account.follower_ids = @all_followers
    account.save
  end
end